package com.xinqi.common.redis.delayed;

import com.xinqi.common.base.error.CommonErrors;
import com.xinqi.common.base.utils.date.Dates;
import com.xinqi.common.redis.delayed.api.DelayQueueName;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBlockingQueue;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RedissonClient;

import java.time.LocalDateTime;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * redisson 延时队列操作类
 */
@Slf4j
@RequiredArgsConstructor
public class DelayQueueHelp {
    private final RedissonClient redissonClient;
    private final Map<String, RBlockingQueue<String>> blockingQueueMap = new ConcurrentHashMap<>();
    private final Map<String, RDelayedQueue<String>> delayedQueueMap = new ConcurrentHashMap<>();

    /**
     * 销毁队列
     */
    public void destroyQueue(String queueName) {
        RDelayedQueue<String> delayedQueue = delayedQueueMap.get(queueName);
        if (delayedQueue != null) {
            delayedQueue.destroy();
        }
    }

    public void destroyQueue(DelayQueueName queueName) {
        destroyQueue(queueName.queueName());
    }

    /**
     * 获取阻塞队列
     *
     * @param queueName 队列名称
     * @return 队列
     */
    private RBlockingQueue<String> getBlockingQueue(String queueName) {
        return blockingQueueMap.computeIfAbsent(queueName, redissonClient::getBlockingQueue);
    }

    /**
     * 获取延时队列
     *
     * @param queueName 队列名称
     * @return 队列
     */
    private RDelayedQueue<String> getDelayQueue(String queueName) {
        return delayedQueueMap.computeIfAbsent(queueName, k -> redissonClient.getDelayedQueue(getBlockingQueue(k)));
    }

    /**
     * 向添加队列数据
     *
     * @param queueName 队列名称
     * @param task      任务
     * @param delay     时间数量
     * @param timeUnit  时间单位
     */
    public void addTask(String queueName, String task, long delay, TimeUnit timeUnit) {
        if (task == null) {
            return;
        }
        RDelayedQueue<String> delayedQueue = getDelayQueue(queueName);
        log.info("Redisson延时队列，添加任务，延时时间:{}({}), queueName:{}, task:{}", delay, timeUnit.name(), queueName, task);
        // 指定时间以后以后将消息发送到指定队列
        delayedQueue.offer(task, delay, timeUnit);
    }

    public void addTask(DelayQueueName queueName, String task, long delay, TimeUnit timeUnit) {
        addTask(queueName.queueName(), task, delay, timeUnit);
    }

    /**
     * 向添加队列数据
     *
     * @param queueName   队列名称
     * @param task        任务
     * @param triggerTime 触发时间
     */
    public void addTask(String queueName, String task, LocalDateTime triggerTime) {
        if (triggerTime == null) {
            throw CommonErrors.BAD_REQUEST.asException("延时任务的触发事件不能为空");
        }
        long delay;
        LocalDateTime now = LocalDateTime.now();
        if (triggerTime.isBefore(now)) {
            delay = 1;
        } else {
            delay = Dates.betweenSeconds(now, triggerTime);
        }
        addTask(queueName, task, delay, TimeUnit.SECONDS);
    }

    public void addTask(DelayQueueName queueName, String task, LocalDateTime triggerTime) {
        addTask(queueName.queueName(), task, triggerTime);
    }

    /**
     * 获取队列数据
     *
     * @param queueName 队列名称
     */
    public String getTask(String queueName) {
        // 不加这行代码，初始化没办法获取到队列数据
        getDelayQueue(queueName);
        RBlockingQueue<String> queue = getBlockingQueue(queueName);
        try {
            return queue.take();
        } catch (InterruptedException e) {
            log.error("Redisson延时队列，获取任务失败！ queueName:{}", queueName, e);
            return null;
        }
    }

    /**
     * 移除队列数据(返回被移除的队列数据)
     *
     * @param queueName 队列名称
     * @param task      任务
     */
    public boolean removeTask(String queueName, String task) {
        RDelayedQueue<String> delayedQueue = getDelayQueue(queueName);
        log.info("Redisson延时队列，移除任务！ queueName:{}, task:{}", queueName, task);
        return delayedQueue.remove(task);
    }

    public boolean removeTask(DelayQueueName queueName, String task) {
        return removeTask(queueName.queueName(), task);
    }

}
